﻿//------------------------------------------------------------------------------
//  Namespace: FruitVentDesign.Controls
//  
//  Function： N/A
//  Name： DateTimePicker
//  
//  Ver       Time                     Author
//  0.10      2021/6/24 14:41:47      FruitVent
//
//  此代码版权归作者本人FruitVent所有
//  源代码使用协议遵循本仓库的开源协议及附加协议，若本仓库没有设置，则按MIT开源协议授权
//  CSDN博客：https://blog.csdn.net/weixin_39552347
//  源代码仓库：https://gitee.com/fruitvent
//  感谢您的下载和使用
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
using FruitVentDesign.Enums;
using FruitVentDesign.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;

namespace FruitVentDesign.Controls
{
    public class DateTimePicker : TextBox
    {
        private static List<string> formatStrings = new List<string>()
        {
            "yyyyMMdd HHmmss",
            "yyyyMMddHHmmss",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy/MM/dd HH:mm:ss",
            "yyyy年MM月dd日 HH:mm:ss",
            "yyyy年MM月dd日 HH时mm分ss秒",
            "dd-MM-yyyy HH:mm:ss",
            "dd/MM/yyyy HH:mm:ss",
            "dd日MM月yyyy年 HH:mm:ss",
            "dd日MM月yyyy年 HH时mm分ss秒",
            "yyyyMMdd hhmmss",
            "yyyyMMddhhmmss",
            "yyyy-MM-dd hh:mm:ss",
            "yyyy/MM/dd hh:mm:ss",
            "yyyy年MM月dd日 hh:mm:ss",
            "yyyy年MM月dd日 hh时mm分ss秒",
            "dd-MM-yyyy hh:mm:ss",
            "dd/MM/yyyy hh:mm:ss",
            "dd日MM月yyyy年 hh:mm:ss",
            "dd日MM月yyyy年 hh时mm分ss秒",
        };

        #region FormatStringProperty 时间格式
        /// <summary>
        /// 转换
        /// </summary>
        public static readonly DependencyProperty FormatStringProperty = DependencyProperty.Register(
            "FormatString", typeof(string), typeof(DateTimePicker), new FrameworkPropertyMetadata("yyyy-MM-dd HH:mm:ss", FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnFormatStringPropertyChanged), new CoerceValueCallback(CoerceFormatString), true, UpdateSourceTrigger.PropertyChanged));

        private static void OnFormatStringPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { }

        static object CoerceFormatString(DependencyObject dobj, object newValue)
        {
            string formatString = "yyyy-MM-dd HH:mm:ss";
            if (newValue is string value && !string.IsNullOrEmpty(value))
            {
                if (value != "yyyy")
                {
                    foreach (string str in formatStrings)
                    {
                        if (str.StartsWith(value))
                        {
                            formatString = str;
                            break;
                        }
                    }
                }
            }
            return formatString;
        }

        public string FormatString
        {
            get { return (string)GetValue(FormatStringProperty); }
            set { SetValue(FormatStringProperty, value); }
        }

        public static void SetFormatString(DependencyObject element, string value)
        {
            element.SetValue(FormatStringProperty, value);
        }

        public static string GetFormatString(DependencyObject element)
        {
            return (string)element.GetValue(FormatStringProperty);
        }
        #endregion

        #region HasErrorMessage 验证通过与否字段
        /// <summary>
        /// 验证通过与否字段
        /// </summary>
        public static readonly DependencyProperty HasErrorMessageProperty = DependencyProperty.Register(
            "HasErrorMessage", typeof(bool), typeof(DateTimePicker), new FrameworkPropertyMetadata(false,
                FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                new PropertyChangedCallback(OnHasErrorMessagePropertyChanged)));

        private static void OnHasErrorMessagePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var oldValue = (object)args.OldValue;
            var newValue = (object)args.NewValue;

            if (oldValue == newValue)
                return;

            if ((bool)newValue)
            {
                if (obj is FrameworkElement content && content.Parent is Form form)
                {
                    if (form.ItemContainerGenerator.ContainerFromItem(content) is FormItem formItem)
                    {
                        formItem.HasErrorMessage = true;
                        var binding = new Binding(nameof(FormItem.ErrorMessage))
                        {
                            Source = content,
                            Mode = BindingMode.TwoWay,
                            Path = new PropertyPath("(Validation.Errors).CurrentItem.ErrorContent")
                        };
                        formItem.SetBinding(FormItem.ErrorMessageProperty, binding);
                    }
                }
            }
            else
            {
                if (obj is FrameworkElement content && content.Parent is Form form)
                {
                    if (form.ItemContainerGenerator.ContainerFromItem(content) is FormItem formItem)
                    {
                        formItem.HasErrorMessage = false;
                    }
                }
            }
        }

        public bool HasErrorMessage
        {
            get { return (bool)GetValue(HasErrorMessageProperty); }
            set { SetValue(HasErrorMessageProperty, value); }
        }

        public static void SetHasErrorMessage(DependencyObject element, bool value)
        {
            element.SetValue(HasErrorMessageProperty, value);
        }

        public static bool GetHasErrorMessage(DependencyObject element)
        {
            return (bool)element.GetValue(HasErrorMessageProperty);
        }
        #endregion

        #region ValueProperty 值
        /// <summary>
        /// 值
        /// </summary>
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            "Value", typeof(DateTime?), typeof(DateTimePicker), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnValuePropertyChanged), new CoerceValueCallback(CoerceValue), true, UpdateSourceTrigger.PropertyChanged));

        private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var oldValue = e.OldValue;
            var newValue = e.NewValue;
            if (oldValue == newValue)
                return;
            (d as DateTimePicker).OnValuePropertyChanged(oldValue, newValue);
        }

        private void OnValuePropertyChanged(object oldValue, object newValue)
        {
            DateTime? value = newValue as DateTime?;

            Text = GetTextDisplay(value);
        }

        public DateTime? Value
        {
            get { return (DateTime?)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public static DateTime? GetValue(DependencyObject d)
        {
            return (DateTime?)d.GetValue(ValueProperty);
        }

        public static void SetValue(DependencyObject obj, DateTime? value)
        {
            obj.SetValue(ValueProperty, value);
        }
        #endregion

        #region PickerProperty 选择器类型
        /// <summary>
        /// 选择器类型
        /// </summary>
        public static readonly DependencyProperty PickerProperty = DependencyProperty.Register(
            "Picker", typeof(DatePickerType), typeof(DateTimePicker), new FrameworkPropertyMetadata(DatePickerType.DateTime));

        public DatePickerType Picker
        {
            get { return (DatePickerType)GetValue(PickerProperty); }
            set { SetValue(PickerProperty, value); }
        }

        public static DatePickerType GetPicker(DependencyObject d)
        {
            return (DatePickerType)d.GetValue(PickerProperty);
        }

        public static void SetPicker(DependencyObject obj, DatePickerType value)
        {
            obj.SetValue(PickerProperty, value);
        }
        #endregion

        public DateTimePicker()
        {
            Loaded += DateTimePicker_Loaded;
        }

        /// <summary>
        /// 组件加载后
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DateTimePicker_Loaded(object sender, RoutedEventArgs e)
        {
            Text = GetTextDisplay(Value);
        }

        /// <summary>
        /// 获取Text的显示
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private string GetTextDisplay(DateTime? value)
        {
            string formatString = FormatString;
            switch (Picker)
            {
                case DatePickerType.Year:
                    if (formatString.StartsWith("yyyy年"))
                    {
                        formatString = "yyyy年";
                    }
                    else
                    {
                        formatString = "yyyy";
                    }
                    break;
                case DatePickerType.Month:
                    if (formatString.StartsWith("yyyy年MM月"))
                    {
                        formatString = "yyyy年MM月";
                    }
                    else
                    {
                        int index = formatString.IndexOf("MM");
                        formatString = formatString.Substring(0, index + 2);
                    }
                    break;
                case DatePickerType.Date:
                    if (formatString.StartsWith("yyyy年MM月dd日"))
                    {
                        formatString = "yyyy年MM月dd日";
                    }
                    else
                    {
                        int index = formatString.IndexOf("dd");
                        formatString = formatString.Substring(0, index + 2);
                    }
                    break;
                case DatePickerType.DateTime:
                    break;
            }
            return value.HasValue ? value.Value.ToString(formatString) : null;
        }

        /// <summary>
        /// 强制转换
        /// </summary>
        /// <param name="dobj"></param>
        /// <param name="newValue"></param>
        /// <returns></returns>
        static object CoerceValue(DependencyObject dobj, object newValue)
        {
            if (newValue is string value && !string.IsNullOrEmpty(value))
            {
                if (IsDate(value))
                {
                    return DateTime.Parse(value);
                }
                else
                {
                    return null;
                }
            }
            if (newValue is DateTime date)
            {
                return date;
            }
            return null;
        }

        /// <summary>
        /// 判断是否可以转换成时间
        /// </summary>
        /// <param name="strDate"></param>
        /// <returns></returns>
        public static bool IsDate(string strDate)
        {
            try
            {
                DateTime.Parse(strDate);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        ///  失去焦点
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);
            Value = (DateTime?)CoerceValue(this, Text);
        }

        /// <summary>
        /// 时间选择器的下拉框
        /// </summary>
        private Popup _popup;

        /// <summary>
        /// 渲染模板
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            if (Template.FindName("PART_Button", this) is System.Windows.Controls.Button PART_Button) PART_Button.Click += PART_Button_Click; ;
            _popup = Template.FindName("PART_Popup", this) as Popup;
        }

        /// <summary>
        /// 时间选择器的下拉按钮点击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PART_Button_Click(object sender, RoutedEventArgs e)
        {
            if (_popup != null)
            {
                if (_popup.IsOpen)
                {
                    _popup.IsOpen = false;
                }

                DateTimePickerDropdown dropdown = new DateTimePickerDropdown(Value)
                {
                    Picker = Picker
                };
                dropdown.DateTimeChanged += Dropdown_DateTimeChanged;
                _popup.Child = dropdown;
                _popup.IsOpen = true;
            }
        }

        /// <summary>
        /// 时间选择启动下拉框的值发生改变
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Dropdown_DateTimeChanged(object sender, EventArgs e)
        {
            DateTime? dt = (DateTime?)(e as DateTimeChangeEventArgs).Data;
            Value = dt;
        }

        #region PlaceholderProperty 占位符
        /// <summary>
        /// 占位符
        /// </summary>
        public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.Register(
            "Placeholder", typeof(string), typeof(DateTimePicker), new FrameworkPropertyMetadata(""));

        public string Placeholder
        {
            get { return (string)GetValue(PlaceholderProperty); }
            set { SetValue(PlaceholderProperty, value); }
        }

        public static string GetPlaceholder(DependencyObject d)
        {
            return (string)d.GetValue(PlaceholderProperty);
        }

        public static void SetPlaceholder(DependencyObject obj, string value)
        {
            obj.SetValue(PlaceholderProperty, value);
        }
        #endregion

        #region CornerRadiusProperty Border圆角
        /// <summary>
        /// Border圆角
        /// </summary>
        public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached(
            "CornerRadius", typeof(CornerRadius), typeof(DateTimePicker), new FrameworkPropertyMetadata(new CornerRadius(3)));

        public CornerRadius CornerRadius
        {
            get { return (CornerRadius)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        public static CornerRadius GetCornerRadius(DependencyObject d)
        {
            return (CornerRadius)d.GetValue(CornerRadiusProperty);
        }

        public static void SetCornerRadius(DependencyObject obj, CornerRadius value)
        {
            obj.SetValue(CornerRadiusProperty, value);
        }
        #endregion

        #region FocusBorderBrushProperty 焦点边框色，输入控件
        /// <summary>
        /// 焦点边框色，输入控件
        /// </summary>
        public static readonly DependencyProperty FocusBorderBrushProperty = DependencyProperty.Register(
            "FocusBorderBrush", typeof(Brush), typeof(DateTimePicker), new FrameworkPropertyMetadata(null));

        public Brush FocusBorderBrush
        {
            get { return (Brush)GetValue(FocusBorderBrushProperty); }
            set { SetValue(FocusBorderBrushProperty, value); }
        }

        public static void SetFocusBorderBrush(DependencyObject element, Brush value)
        {
            element.SetValue(FocusBorderBrushProperty, value);
        }

        public static Brush GetFocusBorderBrush(DependencyObject element)
        {
            return (Brush)element.GetValue(FocusBorderBrushProperty);
        }
        #endregion

        #region MouseOverBorderBrushProperty 鼠标进入边框色，输入控件
        /// <summary>
        /// 鼠标进入边框色，输入控件
        /// </summary>
        public static readonly DependencyProperty MouseOverBorderBrushProperty = DependencyProperty.Register(
            "MouseOverBorderBrush", typeof(Brush), typeof(DateTimePicker), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));

        public Brush MouseOverBorderBrush
        {
            get { return (Brush)GetValue(MouseOverBorderBrushProperty); }
            set { SetValue(MouseOverBorderBrushProperty, value); }
        }

        /// <summary>
        /// Sets the brush used to draw the mouse over brush.
        /// </summary>
        public static void SetMouseOverBorderBrush(DependencyObject obj, Brush value)
        {
            obj.SetValue(MouseOverBorderBrushProperty, value);
        }

        /// <summary>
        /// Gets the brush used to draw the mouse over brush.
        /// </summary>
        public static Brush GetMouseOverBorderBrush(DependencyObject obj)
        {
            return (Brush)obj.GetValue(MouseOverBorderBrushProperty);
        }
        #endregion
    }
}
